Function.RegisterNamespace("Test");

[Import("../../Source/Extensions.js/Function.js")]
[Fixture]
Test.Function=function(){
    [Fixture]
    function Equals(){
        [Fact]
        function ThrowsIfExpectedIsNotAValidFunction(){
            var expected="Function.Equals: 'expected' must be a valid Function pointer.";
            
            var actual=Record.Exception(function(){
                Function.Equals(new Object());
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsFalseIfActualIsNotAValidFunction(){
            var actual=Function.Equals(function(){},new Object());
            
            Assert.False(actual);
        }
        
        [Fact]
        function SetsValueOnReasonIfActualIsNotAValidFunction(){
            var expected="Function.Equals: 'actual' must be a valid Function pointer.";
            var reason={};
            
            Function.Equals(function(){},new Object(),reason);
            var actual=reason.Value;
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsFalseIfFunctionsDontMatch(){
            var actual=Function.Equals(function(a){},function(b){});
            
            Assert.False(actual);
        }
        
        [Fact]
        function SetsReasonIfFunctionsDontMatch(){
            var expectedTarget=function(a){};
            var actualTarget=function(b){};
            var expected=String.Format("Function.Equals: function bodies do not match. Expected '{0}', found '{1}'.",expectedTarget,actualTarget);
            var reason={};
            
            Function.Equals(expectedTarget,actualTarget,reason);
            var actual=reason.Value;
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsTrueIfFunctionsAreSame(){
            var expectedTarget=function(a){};

            var actual=Function.Equals(expectedTarget,expectedTarget);
            
            Assert.True(actual);
        }
        
        [Fact]
        function ReturnsTrueIfFunctionsAreEqual(){
            var expectedTarget=function(a){};
            var actualTarget=function(a){};

            var actual=Function.Equals(expectedTarget,actualTarget);
            
            Assert.True(actual);
        }
    }

    [Fixture]
    function GetBody(){
        [Fact]
        function ThrowsIfMethodIsNotAValidFunction(){
            var expected="Function.GetBody: 'method' must be a valid Function pointer.";
            
            var actual=Record.Exception(function(){
                Function.GetBody(new Object());
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsBodyOfSingleLineFunctionWithoutParameters(){
            var target=function(){return "expected";};
            var expected="return \"expected\";";
            
            var actual=Function.GetBody(target);
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsBodyOfSingleLineFunctionWithParameters(){
            var target=function(one,two,three){return "expected";};
            var expected="return \"expected\";";
            
            var actual=Function.GetBody(target);
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsBodyOfMultiLineFunctionWithoutParameters(){
            var target=function(){
                var one="one";
                var two="two";

                var three="three";
                return "expected";
            };
            var expected=String.Trim(target.toString().slice(target.toString().indexOf('{')+1,target.toString().lastIndexOf('}')));

            var actual=Function.GetBody(target);
            
            Assert.Equal(expected,actual);
        }
    }
    
    [Fixture]
    function GetDelegate(){
        [Fact]
        function ThrowsIfMethodIsNotAValidFunction(){
            var expected="Function.GetDelegate: 'method' must be a valid Function pointer.";
            
            var actual=Record.Exception(function(){
                Function.GetDelegate(new Object());
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsIfInstanceIsUndefined(){
            var expected="Function.GetDelegate: 'instance' must be a valid Object.";

            var actual=Record.Exception(function(){
                Function.GetDelegate(function(){});
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsIfInstanceIsNull(){
            var expected="Function.GetDelegate: 'instance' must be a valid Object.";

            var actual=Record.Exception(function(){
                Function.GetDelegate(function(){},null);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsMethod(){
            var actual=Function.GetDelegate(function(){},{});
        
            Assert.True(Object.IsType(Function,actual));
        }

        [Fact]
        function ReturnsMethodWrappedInDelegate(){
            var expected=function(){
                actual=arguments.callee;
            };
            var actual=null;
            
            Function.GetDelegate(expected,{})();
        
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CallsDelegateMethod(){
            var expected="expected";
            var actual=null;
            
            Function.GetDelegate(function(){
                actual=expected;
            },{})();
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function AppliesDelegateMethodToInstanceProvided(){
            var expected="expected";
            var target={Actual:null};
            
            Function.GetDelegate(function(){
                this.Actual=expected;
            },target)();
            
            Assert.Equal(expected,target.Actual);
        }

        [Fact]
        function UsesConstructedInstanceIfNewedUp(){
            var expected=null;
            var target=Function.GetDelegate(function(){
                expected=this;
            },{});

            var actual=new target();

            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function PreLoadsAdditionalConstructionArguments(){
            var expected="expected";
            var actual=null;

            Function.GetDelegate(function(expected){
                actual=expected;
            },{},expected)();

            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function PassesCallingArguments(){
            var expected="expected";
            var actual=null;

            Function.GetDelegate(function(expected){
                actual=expected;
            },{})(expected);

            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ConcatenatesConstructorAndCallingArguments(){
            var expected="expected";
            var actual=null;

            Function.GetDelegate(function(one,two,three,expected){
                actual=expected;
            },{},1,2)(3,expected);

            Assert.Equal(expected,actual);
        }
    }
    
    [Fixture]
    function GetName(){
        [Fact]
        function ThrowsIfPassedInvalidMethod(){
            var expected="Function.GetName: 'method' must be a valid Function pointer.";
        
            var actual=Record.Exception(function(){
                Function.GetName(new Object());
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ReturnsNameOfMethod(){
            var expected="NamedMethod";
            
            var actual=Function.GetName(function NamedMethod(){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsWhitespaceBeforeNamedMethod(){
            var expected="NamedMethod";
            
            var actual=Function.GetName(function     NamedMethod(){});
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function StripsWhitespaceAfterNamedMethod(){
            var expected="NamedMethod";
            
            var actual=Function.GetName(function NamedMethod     (){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsCarriageReturnBeforeNamedMethod(){
            var expected="NamedMethod";
            
            var actual=Function.GetName(function
            NamedMethod(){});
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function StripsCarriageReturnAfterNamedMethod(){
            var expected="NamedMethod";
            
            var actual=Function.GetName(function NamedMethod
            (){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsTabBeforeNamedMethod(){
            var expected="NamedMethod";
            
            var actual=Function.GetName(function	NamedMethod(){});
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function StripsTabAfterNamedMethod(){
            var expected="NamedMethod";
            
            var actual=Function.GetName(function NamedMethod	(){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsAnonymousForUnnamedMethod(){
            var expected="[anonymous]";
            
            var actual=Function.GetName(function(){});
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function StripsWhitespaceBeforeUnnamedMethod(){
            var expected="[anonymous]";
            
            var actual=Function.GetName(function     (){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsCarriageReturnBeforeUnnamedMethod(){
            var expected="[anonymous]";
            
            var actual=Function.GetName(function
            (){});
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function StripsTabBeforeUnnamedMethod(){
            var expected="[anonymous]";
            
            var actual=Function.GetName(function	(){});
            
            Assert.Equal(expected,actual);
        }
    }

    [Fixture]
    function GetParameters(){
        [Fact]
        function ThrowsIfPassedInvalidMethod(){
            var expected="Function.GetParameters: 'method' must be a valid Function pointer.";
            
            var actual=Record.Exception(function(){
                Function.GetParameters(new Object());
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsEmptyArrayIfNoParametersDefined(){
            var expected=[];
            
            var actual=Function.GetParameters(function(){});
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function StripsWhitespaceFromEmptyParameterList(){
            var expected=[];
            
            var actual=Function.GetParameters(function(     ){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsCarriageReturnFromEmptyParameterList(){
            var expected=[];
            
            var actual=Function.GetParameters(function(
            ){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsTabFromEmptyParameterList(){
            var expected=[];

            var actual=Function.GetParameters(function(	){});

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsArrayOfParameters(){
            var expected=['a','b','c'];
            
            var actual=Function.GetParameters(function(a,b,c){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsWhitespaceBeforeParameters(){
            var expected=['a','b','c'];
            
            var actual=Function.GetParameters(function(     a,     b,     c){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsWhitespaceAfterParameters(){
            var expected=['a','b','c'];
            
            var actual=Function.GetParameters(function(a      ,b     ,c     ){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsCarriageReturnBeforeParameters(){
            var expected=['a','b','c'];
            
            var actual=Function.GetParameters(function(
            a,
            b,
            c){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsCarriageReturnAfterParameters(){
            var expected=['a','b','c'];
            
            var actual=Function.GetParameters(function(a
            ,b
            ,c
            ){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsTabBeforeParameters(){
            var expected=['a','b','c'];
            
            var actual=Function.GetParameters(function(	a,	b,	c){});
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function StripsTabAfterParameters(){
            var expected=['a','b','c'];
            
            var actual=Function.GetParameters(function(a	,b	,c	){});
            
            Assert.Equal(expected,actual);
        }
    }
    
    [Fixture]
    function GetTestable(){
        [Fact]
        function ReturnsMethod(){
            var actual=Function.GetTestable(function(){});
        
            Assert.Type(Function,actual);
        }
        
        [Fact]
        function ReturnsModifiedConstructorIfPassedObject(){
            var expected="expected";
            var actual=null;
            var target=Function.GetTestable(new function(){
                this.expected="expected";
            });
            
            var actual=new target().expected;
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function AddsGetMethodForPrivateMembers(){
            var actual=new (Function.GetTestable(function(){}))();
        
            Assert.True(Object.IsType(Function,actual._GetMember));
        }
        
        [Fact]
        function GetMethodReturnsPrivateMember(){
            var expected="expected";

            var target=Function.GetTestable(function(){
                var hiddenTarget="expected";
            });
            
            var actual=new target()._GetMember("hiddenTarget");
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function AddsSetMethodForPrivateMembers(){
            var target=Function.GetTestable(function(){});
            var actual=new target();
        
            Assert.True(Object.IsType(Function,actual._SetMember));
        }

        [Fact]
        function SetsPrivateMember(){            
            var expected="expected";
            var target=Function.GetTestable(function(){
                var hiddenTarget=null;
                this.GetHiddenTarget=function(){
                    return hiddenTarget;
                }
            });
        
            target=new target();
            target._SetMember("hiddenTarget",expected);
            var actual=target.GetHiddenTarget();
        
            Assert.Equal(expected,actual);
        }
    }

    [Fixture]
    function IsEmpty(){
        [Fact]
        function ThrowsIfMethodIsNotAFunction(){
            var expected="Function.IsEmpty: 'method' must be a valid Function pointer.";

            var actual=Record.Exception(function(){
                Function.IsEmpty({});
            });

            Assert.Equal(expected,actual);
        }

        [Fact]
        function ReturnsFalseIfFunctionHasBody(){
            var target=function(){return true;};

            var actual=Function.IsEmpty(target);

            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfMultilineFunctionHasBody(){
            var target=function(){
                var test="test";

                return test;
            };

            var actual=Function.IsEmpty(target);

            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfMultilineFunctionHasBodyWithComments(){
            var target=function(){
                var test="test";
                // This is a comment
                return test;
                /*
                
                    So is this.

                */
            };

            var actual=Function.IsEmpty(target);

            Assert.False(actual);
        }

        [Fact]
        function ReturnsTrueIfFunctionHasOnlyLineComments(){
            var target=function(){
                // This is a comment

                // So is this.
            };

            var actual=Function.IsEmpty(target);

            Assert.True(actual);
        }

        [Fact]
        function ReturnsTrueIfFunctionHasOnlyBlockComments(){
            var target=function(){
                /* This is a comment */

                /*
                
                So is this.

                */
            };

            var actual=Function.IsEmpty(target);

            Assert.True(actual);
        }

        [Fact]
        function ReturnsTrueIfFunctionHasOnlyComments(){
            var target=function(){
                // This is a comment 

                /*
                
                So is this.

                */
            };

            var actual=Function.IsEmpty(target);

            Assert.True(actual);
        }

    }

    [Fixture]
    function IsNamespace(){
        [Fact]
        function ReturnsFalseIfTargetIsNull(){
            var target=null;

            var actual=Function.IsNamespace(target);

            Assert.False(actual);        
        }

        [Fact]
        function ReturnsFalseIfTargetIsUndefined(){
            var actual=Function.IsNamespace();

            Assert.False(actual);        
        }

        [Fact]
        function ReturnsFalseIfTargetIsAnArray(){
            var target=[];

            var actual=Function.IsNamespace(target);

            Assert.False(actual);        
        }

        [Fact]
        function ReturnsFalseIfTargetIsABoolean(){
            var target=true;

            var actual=Function.IsNamespace(target);

            Assert.False(actual);        
        }
    
        [Fact]
        function ReturnsFalseIfTargetIsAnError(){
            var target=new Error();

            var actual=Function.IsNamespace(target);

            Assert.False(actual);        
        }

        [Fact]
        function ReturnsFalseIfTargetIsAFunction(){
            var target=function(){};

            var actual=Function.IsNamespace(target);

            Assert.False(actual);        
        }

        [Fact]
        function ReturnsFalseIfTargetIsANumber(){
            var target=7357;

            var actual=Function.IsNamespace(target);

            Assert.False(actual);        
        }

        [Fact]
        function ReturnsFalseIfTargetIsAString(){
            var target="test";

            var actual=Function.IsNamespace(target);

            Assert.False(actual);        
        }

        [Fact]
        function ReturnsFalseIfTargetHasNoConstructor(){
            var target={};
            target.constructor=null;

            var actual=Function.IsNamespace(target);

            Assert.False(actual);
        }

        [Fact]
        function ReturnsFalseIfTargetConstructorDoesNotHaveNameSpaceFlagSet(){
            var target={};

            var actual=Function.IsNamespace(target);

            Assert.False(actual);
        }

        [Fact]
        function ReturnsTrueIfTargetConstructorHasNameSpaceFlagSet(){
            var targetRoot={};
            Function.RegisterNamespace("target",targetRoot);

            var actual=Function.IsNamespace(targetRoot.target);

            Assert.True(actual);
        }
    }
    
    [Fixture]
    function RegisterNamespace(){
        [Fact]
        function ThrowsIfNamespaceIsNotConvertibleToString(){
            var expected="Function.RegisterNamespace: 'path' must be convertible to String.";
        
            var actual=Record.Exception(function(){
                Function.RegisterNamespace(null);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function StartsNameSpaceFromRootContainer(){
            var target={};
            
            Function.RegisterNamespace("Namespace",target);
            
            Assert.NotNull(target.Namespace);
        }

        [Fact]
        function StartsNameSpaceFromGlobalObjectIfRootContainerNotSpecified(){
            var target={};
            
            Function.RegisterNamespace("Actual");
            
            Assert.NotNull(Actual);
            delete Actual;
        }
        
        [Fact]
        function CreatesNestedNameSpaces(){
            var target={};
            
            Function.RegisterNamespace("Namespace.NestedNamespace",target);
            
            Assert.NotNull(target.Namespace.NestedNamespace);
        }

        [Fact]
        function MarksNamespacePrototype(){
            var target={};
            
            Function.RegisterNamespace("Namespace",target);
            
            Assert.True(target.Namespace.constructor.IsNamespace);
        }
    }
    
    [Fixture]
    function Inherit(){
        [Fact]
        function ThrowsIfTypeIsNotAValidFunction(){
            var target=function(){};
            var expected=String.Format("{0}.Inherit: 'type' must be a valid Function pointer.",Function.GetName(target));
            
            var actual=Record.Exception(function(){
                target.Inherit(new Object());
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsNameOverrideIfTypeIsNotAValidFunction(){
            var target=function(){};
            var name='NameOverride';
            var expected=String.Format("{0}.Inherit: 'type' must be a valid Function pointer.",name);
            
            var actual=Record.Exception(function(){
                target.Inherit(new Object(),name);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsIfBaseAlreadyDeclared(){
            var targetBase=function targetBase(){};
            var targetType=function targetType(){};
            var target=function target(){};
            target.Inherit(targetBase);
            var expected=String.Format("{0}.Inherit: unable to inherit {1}. {0} already inherits {2}.",Function.GetName(target),Function.GetName(targetType),Function.GetName(targetBase));

            var actual=Record.Exception(function(){
                target.Inherit(targetType);
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function SetsTargetConstructorOnFunctionInstance(){
            var expected=function(){};
            
            expected.Inherit(function(){});
            var actual=new expected().constructor;
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function SetsBaseTypeOnFunctionDefinition(){
            var target=function target(){};
            var expected=function expected(){};
            
            target.Inherit(expected);
            var actual=target.base;
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function SetsBaseConstructorOnFunctionInstance(){
            var target=function(){};
            
            target.Inherit(function(){});
            var actual=Object.IsType(Function,new target().base);
            
            Assert.True(actual);
        }

        [Fact]
        function SetsConstructorBaseToInheritedType(){
            var target=function(){
                this.base();
            };
            var expected=function(){var expected;};
            
            target.Inherit(expected);
            var actual=new target().constructor.base;
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function SetsInstanceBaseConstructorToInheritedType(){
            var target=function(){
                this.base();
            };
            var expected=function(){var expected;};
            
            target.Inherit(expected);
            var actual=new target().base.constructor;
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function CallingBaseConstructorSetsBaseMethodsOnInstance(){
            var target=function(){
                this.base();
            };
            var targetBase=function(){
                this.BaseMethod=function(){}
            };
            
            target.Inherit(targetBase);
            var actual=new target();
            
            Assert.True(Object.IsType(Function,actual.BaseMethod));
        }

        [Fact]
        function CallingBaseConstructorSetsBaseMethodDelegatesOnBaseConstructor(){
            var target=function(){
                this.base();
            };
            var targetBase=function(){
                this.BaseMethod=function(){}
            };
            
            target.Inherit(targetBase);
            var actual=new target();
            
            Assert.True(Object.IsType(Function,actual.base.BaseMethod));
        }

        [Fact]
        function CanCallBaseMethodOnSubClass() {
            var expected="expected";
            var target = function () {
                this.base();
            };
            var targetBase = function () {
                this.BaseMethod = function () { 
                    return expected;
                }
            };

            target.Inherit(targetBase);
            var actual = new target().BaseMethod();

            Assert.Equal(expected, actual);
        }

        [Fact]
        function CanCallBaseMethodOnBaseClass() {
            var expected="expected";
            var target = function () {
                this.base();
            };
            var targetBase = function () {
                this.BaseMethod = function () { 
                    return expected;
                }
            };

            target.Inherit(targetBase);
            var actual = new target().base.BaseMethod();

            Assert.Equal(expected, actual);
        }

        [Fact]
        function CanCallOverriddenBaseMethodOnBaseClass() {
            var expected="expected";
            var target = function () {
                this.base();

                this.BaseMethod=function(){
                    throw new Error("Test.Function.Inherit.CanCallOverriddenBaseMethodOnBaseClass: called BaseMethod on sub class.");
                }
            };
            var targetBase = function () {
                this.BaseMethod = function () { 
                    return expected;
                }
            };

            target.Inherit(targetBase);
            var actual = new target().base.BaseMethod();

            Assert.Equal(expected, actual);
        }

        [Fact]
        function CanCallOverriddenBaseMethodOnBaseWithRootClass() {
            var expected="expected";
            function targetRoot() {
                new function(){
                    return "Root";
                }

                this.BaseMethod = function () { 
                    throw new Error("Test.Function.Inherit.CanCallOverriddenBaseMethodOnBaseClass: called BaseMethod on root class.");
                }
            };
            function targetBase() {
                this.base();

                this.BaseMethod = function () { 
                    return expected;
                }
            };
            targetBase.Inherit(targetRoot);
            function targetChild() {
                this.Tag="tag";

                this.base();
                
                this.BaseMethod=function(){
                    throw new Error("Test.Function.Inherit.CanCallOverriddenBaseMethodOnBaseClass: called BaseMethod on sub class.");
                }
            };
            targetChild.Inherit(targetBase);
            var target = new targetChild();

            var actual=target.base.BaseMethod();

            Assert.Equal(expected, actual);
        }


        [Fact]
        function CanCallOverriddenRootMethodOnRootClass() {
            var expected="expected";
            function targetRoot() {
                this.BaseMethod = function () { 
                    return expected;
                }
            };

            function targetBase () {
                this.base();

                this.BaseMethod = function () { 
                    throw new Error("Test.Function.Inherit.CanCallOverriddenBaseMethodOnBaseClass: called BaseMethod on base class.");
                }
            };
            targetBase.Inherit(targetRoot);

            function targetChild() {
                this.base();
                
                this.BaseMethod=function(){
                    throw new Error("Test.Function.Inherit.CanCallOverriddenBaseMethodOnBaseClass: called BaseMethod on sub class.");
                }
            };
            targetChild.Inherit(targetBase);
            var target=new targetChild();

            var actual = target.base.base.BaseMethod();

            Assert.Equal(expected, actual);
        }

        [Fact]
        function CallingBaseMethodSetsScopeToInheritedInstance(){
            function target(){
                this.base();
            };
            function targetBase(){
                this.BaseMethod=function(){
                    return this;
                }
            };            
            target.Inherit(targetBase);
            var expected=new target();
            
            var actual=expected.base.BaseMethod();

            Assert.Same(expected,actual);
        }

        [Fact]
        function ConstructorSetsContextBaseToBaseClassInstance(){
            function targetBase(){}
            function targetChild(){
                actual=this.base;
                this.base();
            }
            targetChild.Inherit(targetBase);
            var actual=null;

            var target=new targetChild();

            Assert.Equal(target.base,actual)
        }

        [Fact]
        function ConstructorSetsContextBaseToRootClassInstance(){
            function targetRoot(){
            }
            function targetBase(){
                actual=this.base;
            }
            targetBase.Inherit(targetRoot);
            function targetChild(){
                this.base();
            }
            targetChild.Inherit(targetBase);
            var actual=null;

            var target=new targetChild();

            Assert.Equal(target.base.base,actual);
        }

        [Fact]
        function CallingMethodSetsContextBaseToBaseClassInstance(){
            function targetBase(){}
            function targetChild(){
                this.Target=function(){
                    actual=this.base;
                }
            }
            targetChild.Inherit(targetBase);
            var actual=null;
            var target=new targetChild();

            target.Target();

            Assert.Equal(target.base,actual);
        }

        [Fact]
        function BaseMethodIsNotCreatedIfBaseConstructorIsNotInvoked(){
            function targetRoot(){}
            function targetBase(){
                this.Target=function(){}
            }
            targetBase.Inherit(targetRoot);
            function targetChild(){
                actual=this.base.Target;
            }
            targetChild.Inherit(targetBase);
            var actual=null;

            new targetChild();

            Assert.Undefined(actual);
        }

        [Fact]
        function BaseMethodIsCreatedIfBaseConstructorIsInvoked(){
            function targetRoot(){}
            function targetBase(){
                this.Target=function(){}
            }
            targetBase.Inherit(targetRoot);
            function targetChild(){
                this.base();
                actual=this.base.Target;
            }
            targetChild.Inherit(targetBase);
            var actual=null;

            new targetChild();

            Assert.Type(Function,actual);
        }

        [Fact]
        function CallingBaseMethodSetsContextBaseToRootClassInstance(){
            function targetRoot(){}
            function targetBase(){
                this.Target=function(){
                    actual=this.base;                
                }
            }
            targetBase.Inherit(targetRoot);
            function targetChild(){
                this.base();

                this.Target=function(){
                    this.base.Target();
                }
            }
            targetChild.Inherit(targetBase);
            var actual=null;
            var target=new targetChild();

            target.Target();

            Assert.Equal(target.base.base,actual);
        }

        [Fact]
        function CallingRootMethodClearsContextBase(){
            function targetRoot(){
                this.Target=function(){                
                    actual=this.base;
                }
            }
            function targetBase(){
                this.base();
                
                this.Target=function(){
                    this.base.Target();                
                }
            }
            targetBase.Inherit(targetRoot);
            function targetChild(){
                this.base();
                
                this.Target=function(){
                    this.base.Target();
                }
            }
            targetChild.Inherit(targetBase);
            var actual=null;
            var target=new targetChild();

            target.Target();

            Assert.Null(actual);
        }

        [Fact]
        function MaintainsIsolatedScopeInBaseClass(){
            var expected="expected";
            function targetRoot(){}
            function targetBase(){
                var _expected="local";
                this.base();
                this.GetExpected=function(){
                    actual=_expected;
                }
                this.SetExpected=function(value){
                    _expected=value;
                }
            }
            targetBase.Inherit(targetRoot);
            function targetOne(){
                this.base();
                this.SetExpected(expected);
            }
            function targetTwo(){
                this.base();
                this.SetExpected("unexpected");
            }
            targetOne.Inherit(targetBase);
            targetTwo.Inherit(targetBase);
            var actual=null;
            var target=new targetOne();
            new targetTwo().GetExpected();

            target.GetExpected();

            Assert.Equal(expected,actual);

        }
    }
    
    [Fixture]
    function Implement(){
        [Fact]
        function DecoratesBlankObjectWithConstructor(){
            var target=function(){};
            
            var actual=Record.Exception(function(){
                target.Implement(new target());
            });
            
            Assert.Null(actual);
        }
        
        [Fact]
        function ThrowsIfUnableToInstantiateConstructor(){
            var nestedError="nestedError";
            var target=function(){throw new Error(nestedError)};
            var expected=String.Format("{0}.Implement: unable to instantiate constructor. {1}",Function.GetName(target),nestedError);
            
            var actual=Record.Exception(function(){
                target.Implement({expected:function(){}});
            });
            
            Assert.Equal(expected,actual);
        }

        [Fact]
        function ThrowsNameOverrideIfUnableToInstantiateConstructor(){
            var nestedError="nestedError";
            var target=function(){throw new Error(nestedError)};
            var targetName="targetName";
            var expected=String.Format("{0}.Implement: unable to instantiate constructor. {1}",targetName,nestedError);
            
            var actual=Record.Exception(function(){
                target.Implement({expected:function(){}},targetName);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function IgnoresPropertiesThatAreNotFunctionsForNow(){
            var target=function(){};
            
            var actual=Record.Exception(function(){
                target.Implement({target:"target"});
            });
            
            Assert.Null(actual);
        }
        
        [Fact]
        function DoesNotThrowIfPropertyOfTypeInstanceIsFoundOnTargetMethodPrototype(){
            var target=function(){};
            target.prototype.Expected=function(){};
            
            var actual=Record.Exception(function(){
                target.Implement({Expected:function(){}});
            });
                   
            Assert.Null(actual);
        }

        [Fact]
        function DoesNotThrowIfPropertyOfTypeInstanceIsFoundOnInstanceOfTargetMethod(){
            var expectedProperty="expected";
            var target=function(){
                this[expectedProperty]=function(){};
            }
            var targetType={};
            targetType[expectedProperty]=function(){};
            
            var actual=Record.Exception(function(){
                target.Implement(targetType);
            });

            Assert.Null(actual);
        }
        
        [Fact]
        function ThrowsIfPropertyOfTypeInstanceIsNotFoundOnTargetMethod(){
            var expectedProperty="expected";
            var target=function(){};
            var targetType={};
            targetType[expectedProperty]=function(){};
            var expected=String.Format("{0}.Implement: does not implement interface member '{1}'",Function.GetName(target),expectedProperty);
            
            var actual=Record.Exception(function(){
                target.Implement(targetType);
            });
            
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function DoesNotInvokeConstructorWhenSatisfiedByPrototype(){
            var expectedProperty="expected";
            var target=function(){
                throw new Error("FunctionTest.Implement.DoesNotInvokeConstructorWhenSatisfiedByPrototype: Constructor was called.");
            };
            target.prototype[expectedProperty]=function(){};
            var targetType={};
            targetType[expectedProperty]=function(){};
            
            var actual=Record.Exception(function(){
                target.Implement(targetType);
            });
                   
            Assert.Null(actual);
        }

        [Fact]
        function InvokesConstructorWhenNotSatisfiedByPrototype(){
            var expected="expected";
            var actual=null;
            var target=function(){
                actual=expected;
                this[expected]=function(){}
            };
            var targetType={};
            targetType[expected]=function(){};
            
            target.Implement(targetType);
                   
            Assert.Equal(expected,actual);
        }
        
        [Fact]
        function ThrowsIfExpectedParamsDontMatchActualParams(){
            var expectedProperty="expected";
            var actualParams=['a','b','c'];
            var target=function targetName(){
                this[expectedProperty]=new Function(actualParams[0],actualParams[1],actualParams[2],'');
            };
            var expectedParams=actualParams.slice(0,2);
            var targetType={};
            targetType[expectedProperty]=new Function(expectedParams[0],expectedParams[1],'');
            var expected=String.Format("{0}.Implement: The signature '{0}.{2}({1})' does not match interface member '{4}.{2}({3})'",Function.GetName(target),actualParams,expectedProperty,expectedParams,Function.GetName(targetType.constructor))
            
            var actual=Record.Exception(function(){
                target.Implement(targetType);
            });
            
            Assert.Equal(expected,actual);
        }        
    }
}